#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <array>
#include <vector>
#include <map>
#include <iostream>
#include <fstream>
#include <bitset>

#include <netinet/in.h>  // sockaddr_in
#include <sys/types.h>   // socket
#include <sys/socket.h>  // socket

#include <pthread.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "seeta_face2_detect_adpt.h"

#include "FaceEngine.h"
#include "Struct_cv.h"

#define SERVER_PORT 8000
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 16

const float threshold = 0.5f;

typedef struct 
{
	seeta::FaceEngine *hpclFaceEngine;
	
}SEETA_FACE2_INSTANCE_CTX_S;

std::vector<std::string> GalleryImageFilename;
std::map<int,int> GalleryIndexMap;

// int get_extension(std::string fname){      
//     char c = fname.at(fname.length()-1);
//     char c2 = fname.at(fname.length()-3);
//     if ((c == 'f') && (c2 == 'g')){    // file extension name is gif
//         return 1;
//     }else if ((c == 'g') && (c2 == 'j')){ // file extension name is jpg
//         return 2;
//     }else if ((c == 'g') && (c2 == 'p')){ // file extension name is png
//         return 3;
//     }else if ((c == 'p') && (c2 == 'b')){ // file extension name is bmp
//         return 4;
//     }
//     return 0;
// }

// SeetaImageData readImage(std::string filename){
// 	SeetaImageData image;
// 	std::ifstream ffin(filname.c_str(), std::ifstream::in|std::ios::binary);
// 	int result = get_extension(fname);
//     char s1[2] = {0}, s2[2] = {0};
//     switch(result)
//     {
//     case 1:    // gif   
//         ffin.seekg(6);        
//         ffin.read(s1, 2);
//         ffin.read(s2, 2);         
//         break;
//     case 2:    // jpg
//         ffin.seekg(164);       
//         ffin.read(s1, 2);
//         ffin.read(s2, 2);       
//         break;
//     case 3:     // png
//         ffin.seekg(17);       
//         ffin.read(s1, 2);
//         ffin.seekg(2, std::ios::cur);
//         ffin.read(s2, 2);      
//         break;
//     case 4:     // bmp      
//         ffin.seekg(18);       
//         ffin.read(s1, 2);
//         ffin.seekg(2, std::ios::cur);
//         ffin.read(s2, 2);      
//         break;
//     default:
//         cout<<"NO"<<endl;
//         break;
//     }   
//     image.width = (unsigned int)(s1[1])<<8|(unsigned int)(s1[0]);
//     image.height = (unsigned int)(s2[1])<<8|(unsigned int)(s2[0]);

//     ffin.close();
// }

static int ConvYuvFrmToBgr888(const VIDEO_FRAME_INFO_S *pstYuv420spPic, 
		HI_U64 u64Bgr888PicBufPhyAddr, char *pcBgr888PicBufVirAddr)
{
    int iRet = HI_FAILURE;
	IVE_HANDLE hIve;
	IVE_SRC_IMAGE_S stIveSrcImg;
	IVE_DST_IMAGE_S stIveDstImg;
	IVE_CSC_CTRL_S stIveCscCtrl;
	HI_BOOL bFinish = HI_FALSE;
	
	memset(&stIveSrcImg, 0, sizeof(stIveSrcImg));
	stIveSrcImg.enType = IVE_IMAGE_TYPE_YUV420SP;
	stIveSrcImg.au64PhyAddr[0] = pstYuv420spPic->stVFrame.u64PhyAddr[0];
	stIveSrcImg.au64PhyAddr[1] = pstYuv420spPic->stVFrame.u64PhyAddr[1];
	stIveSrcImg.au64PhyAddr[2] = pstYuv420spPic->stVFrame.u64PhyAddr[2];
	stIveSrcImg.au64VirAddr[0] = (HI_U64)(pstYuv420spPic->stVFrame.u64VirAddr[0]);
	stIveSrcImg.au64VirAddr[1] = (HI_U64)(pstYuv420spPic->stVFrame.u64VirAddr[1]);
	stIveSrcImg.au64VirAddr[2] = (HI_U64)(pstYuv420spPic->stVFrame.u64VirAddr[2]);
	stIveSrcImg.au32Stride[0] = pstYuv420spPic->stVFrame.u32Stride[0];
	stIveSrcImg.au32Stride[1] = pstYuv420spPic->stVFrame.u32Stride[1];
	stIveSrcImg.au32Stride[2] = pstYuv420spPic->stVFrame.u32Stride[2];
	stIveSrcImg.u32Width = pstYuv420spPic->stVFrame.u32Width;
	stIveSrcImg.u32Height = pstYuv420spPic->stVFrame.u32Height;

	memset(&stIveDstImg, 0, sizeof(stIveDstImg));
	stIveDstImg.enType = IVE_IMAGE_TYPE_U8C3_PACKAGE;
	stIveDstImg.au64PhyAddr[0] = u64Bgr888PicBufPhyAddr;
	stIveDstImg.au64VirAddr[0] = (HI_U64)pcBgr888PicBufVirAddr;
	stIveDstImg.au32Stride[0] = stIveSrcImg.u32Width;
	stIveDstImg.u32Width = stIveSrcImg.u32Width;
	stIveDstImg.u32Height = stIveSrcImg.u32Height;

	memset(&stIveCscCtrl, 0, sizeof(stIveCscCtrl));
	stIveCscCtrl.enMode = IVE_CSC_MODE_PIC_BT709_YUV2RGB;
	iRet = HI_MPI_IVE_CSC(&hIve, &stIveSrcImg, &stIveDstImg, &stIveCscCtrl, HI_TRUE);
	if (HI_SUCCESS != iRet)
	{
		HI_ERR_PRINT("HI_MPI_IVE_CSC() failed.\n");
		goto EXIT;
	}

	iRet = HI_MPI_IVE_Query(hIve, &bFinish, HI_TRUE);
	if ((HI_SUCCESS == iRet) && (!bFinish))
	{
		iRet = HI_FAILURE;
	}

EXIT:
	return iRet;
}

int SEETA_FACE2_ADPT_CreateInstance(RT_HANDLE *phInstance)
{
	int iRet = HI_FAILURE;
	SEETA_FACE2_INSTANCE_CTX_S *hpstInstanceCtx;
	struct SeetaModelSetting stModelSetting[3];
	const char *fdapszModel[2];
	const char *pdapszModel[2];
	const char *frapszModel[2];

	hpstInstanceCtx = (SEETA_FACE2_INSTANCE_CTX_S*)malloc(sizeof(SEETA_FACE2_INSTANCE_CTX_S));
	if (NULL == hpstInstanceCtx)
	{
		goto EXIT;
	}

	fdapszModel[0] = "/mnt/app/resource/model/fd_2_00.dat";
	fdapszModel[1] = NULL;
	stModelSetting[0].device = SEETA_DEVICE_CPU;
	stModelSetting[0].id = 0;
	stModelSetting[0].model = fdapszModel;
	seeta::ModelSetting FD_model(stModelSetting[0]);
	pdapszModel[0] = "/mnt/app/resource/model/pd_2_00_pts5.dat";
	pdapszModel[1] = NULL;
	stModelSetting[1].device = SEETA_DEVICE_CPU;
	stModelSetting[1].id = 0;
	stModelSetting[1].model = pdapszModel;
	seeta::ModelSetting PD_model(stModelSetting[1]);
	frapszModel[0] = "/mnt/app/resource/model/fr_2_10.dat";
	frapszModel[1] = NULL;
	stModelSetting[2].device = SEETA_DEVICE_CPU;
	stModelSetting[2].id = 0;
	stModelSetting[2].model = frapszModel;
	seeta::ModelSetting FR_model(stModelSetting[2]);
	hpstInstanceCtx->hpclFaceEngine = new seeta::FaceEngine( FD_model, PD_model, FR_model);
	if (NULL == hpstInstanceCtx->hpclFaceEngine)
	{
		HI_ERR_PRINT("new failed.\n");
		goto EXIT_WITH_FREE;
	}
	
	hpstInstanceCtx->hpclFaceEngine->FD.set(seeta::FaceDetector::PROPERTY_MIN_FACE_SIZE, 100);
	//hpstInstanceCtx->hpclFaceDetector->set(seeta::FaceDetector::PROPERTY_VIDEO_STABLE, 1);
	//hpstInstanceCtx->hpclFaceDetector->set(seeta::FaceDetector::PROPERTY_THRESHOLD1, 1.0);
	//hpstInstanceCtx->hpclFaceDetector->set(seeta::FaceDetector::PROPERTY_THRESHOLD2, 1.0);
	hpstInstanceCtx->hpclFaceEngine->FD.set(seeta::FaceDetector::PROPERTY_THRESHOLD3, 1.0);

	iRet = HI_SUCCESS;
	std::fstream f("/mnt/app/resource/model/pics.txt");
	while(!f.eof()){
		static char file[128];
		std::string str,filename;
		f >> str;
		if(str.size() == 0) continue;
		sprintf(file,"/mnt/app/resource/model/%s\0",str.c_str());
		int len = strlen(file);
		for(int i = 0;i<len; i++){
			filename += file[i]; 
		}
		printf("%s\n",str.c_str());
		GalleryImageFilename.push_back(filename);
	}
	for(auto &filename:GalleryImageFilename){
		static int cntPic = 0;
		int cnt = 0,index;
		seeta::cv::ImageData image = cv::imread( filename );
		while((index = hpstInstanceCtx->hpclFaceEngine->Register(image)) == -1 && cnt < 10) {
			cnt++, printf("register %s failed!!\n",filename.c_str());
		}
		if(cnt >= 10) continue;
		printf("register %s successful!!\n",filename.c_str());
		GalleryIndexMap[index] = cntPic++;
	}
	*phInstance = (RT_HANDLE)hpstInstanceCtx;
	puts("successful build");
	goto EXIT;

EXIT_WITH_FREE:
	free(hpstInstanceCtx);
EXIT:  
    return iRet;
}
int server_socket_fd;
struct sockaddr_in server_addr;
int socket_init(const char* IP){

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    if(inet_pton(AF_INET, IP, &server_addr.sin_addr) == 0)
    {
        perror("Server IP Address Error:");
        exit(1);
    }
    server_addr.sin_port = htons(SERVER_PORT);

    // 创建socket，若成功，返回socket描述符
    server_socket_fd = socket(PF_INET, SOCK_STREAM, 0);
    if(server_socket_fd < 0)
    {
        perror("Create Socket Failed");
        return 1;
    }
    int opt = 1;
    setsockopt(server_socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 绑定socket和socket地址结构
    if(-1 == (bind(server_socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr))))
    {
        perror("Server Bind Failed");
        return 1;
    }
    
    // socket监听
    if(-1 == (listen(server_socket_fd, LENGTH_OF_LISTEN_QUEUE)))
    {
        perror("Server Listen Failed");
        return 1;
    }
	return 0;
}

struct send_socket_arg{
	int64_t id;
	double similarity;
};

void* send_socket(void *arg){
		pthread_detach(pthread_self());
		int64_t id = ((send_socket_arg * )arg)->id;
		double similarity = ((send_socket_arg * )arg)->similarity;
		
	    // 定义客户端的socket地址结构
        struct sockaddr_in client_addr;
        socklen_t client_addr_length = sizeof(client_addr);

        // 接受连接请求，返回一个新的socket(描述符)，这个新socket用于同连接的客户端通信
        // accept函数会把连接到的客户端信息写到client_addr中
        int new_server_socket_fd = accept(server_socket_fd, (struct sockaddr*)&client_addr, &client_addr_length);
        if(new_server_socket_fd < 0)
        {
            perror("Server Accept Failed");
            pthread_exit(1);
        }

        // recv函数接收数据到缓冲区buffer中
        static char buffer[BUFFER_SIZE];
        bzero(buffer, BUFFER_SIZE);
        int64_t *idp = (int64_t *)buffer;
		double *flp = (double *)buffer;
		idp[0] = id;
		flp[1] = similarity;

        if(send(new_server_socket_fd, buffer, BUFFER_SIZE, 0) < 0)
        {
            perror("Send File Name Failed:");
            pthread_exit(1);
        }

        // 关闭与客户端的连接
        close(new_server_socket_fd);
		pthread_exit(0);
}

int SEETA_FACE2_ADPT_SendPic(RT_HANDLE hInstance, const VIDEO_FRAME_INFO_S *pstCatchYuvFrm, 
		RECT_S *pstObjRect, HI_U32 *pu32RectNum)
{
	int iRet = HI_FAILURE;
	SEETA_FACE2_INSTANCE_CTX_S *hpstInstanceCtx;
	//vector<FaceRect> stFaceRectVector;
	int iDetectedFaceNum;
	HI_U32 u32Idx;
	struct SeetaImageData simage;
	//SeetaFaceInfoArray faces;
	HI_U64 u64Bgr888PicBufPhyAddr;
	char *pcBgr888PicBufVirAddr;
	HI_U32 u32BufLen;
	HI_U32 u32ObjRectNum;

	hpstInstanceCtx = (SEETA_FACE2_INSTANCE_CTX_S*)hInstance;

	u32ObjRectNum = *pu32RectNum;
	*pu32RectNum = 0;
	//printf("------0000000000000------\n");

	u32BufLen = pstCatchYuvFrm->stVFrame.u32Width * pstCatchYuvFrm->stVFrame.u32Height * 3;
	iRet = HI_MPI_SYS_MmzAlloc_Cached(&u64Bgr888PicBufPhyAddr, 
			(HI_VOID**)&pcBgr888PicBufVirAddr, NULL, NULL, u32BufLen);
	if (HI_SUCCESS != iRet)
	{	
		printf("HI_MPI_SYS_MmzAlloc_Cached() failed.\n");
		goto EXIT;
	}
	iRet = ConvYuvFrmToBgr888(pstCatchYuvFrm, u64Bgr888PicBufPhyAddr, pcBgr888PicBufVirAddr);
	if (HI_SUCCESS != iRet)
	{
		printf("ConvYuvFrmToBgr888() failed.\n");
		goto EXIT_WITH_FREE;
	}
	HI_MPI_SYS_MmzFlushCache(u64Bgr888PicBufPhyAddr, pcBgr888PicBufVirAddr, u32BufLen);
	
	simage.data = (unsigned char *)pcBgr888PicBufVirAddr;
	simage.width = pstCatchYuvFrm->stVFrame.u32Width;
	simage.height = pstCatchYuvFrm->stVFrame.u32Height;
	simage.channels = 3;
	std::vector<SeetaFaceInfo>  faces = hpstInstanceCtx->hpclFaceEngine->DetectFaces(simage);

	for(SeetaFaceInfo face:faces){
		printf("successful detect\n");
		int64_t index = -1;
        float similarity = 0;
		auto points = hpstInstanceCtx->hpclFaceEngine->DetectPoints(simage, face);
        auto queried = hpstInstanceCtx->hpclFaceEngine->QueryTop( simage, points.data(), 1, &index, &similarity );
		printf("%lld %f\n",index,similarity);
		if(similarity > threshold){
			pthread_t pid;
			send_socket_arg args;
			args.id = GalleryIndexMap[index], args.similarity = similarity;
			pthread_create(&pid,nullptr,send_socket,(void *)&args);
			pstObjRect[*pu32RectNum].s32X = face.pos.x;
			pstObjRect[*pu32RectNum].s32Y = face.pos.y;
			pstObjRect[*pu32RectNum].u32Width = (HI_U32)face.pos.width;
			pstObjRect[*pu32RectNum].u32Height = (HI_U32)face.pos.height;
			printf("successful check\n");
			(*pu32RectNum)++;
		}
	}

	// iDetectedFaceNum = (int)faces.size;
	// if (iDetectedFaceNum <= 0)
	// {
	// 	//printf("size(%u, %u) iDetectedFaceNum = 0.\n", pstCatchYuvFrm->stVFrame.u32Width, pstCatchYuvFrm->stVFrame.u32Height);
	// 	goto EXIT_WITH_FREE;
	// }
	
	// u32ObjRectNum = RT_MIN(iDetectedFaceNum, u32ObjRectNum);
	// for (u32Idx = 0; u32Idx < u32ObjRectNum; ++u32Idx)
	// {
	// #if 1
	// 	printf("\033[0;32m""ObjIdx(%u) stRect(%d, %d, %d, %d) Score(%d).\n""\033[0m", u32Idx, 
	// 			faces.data[u32Idx].pos.x, faces.data[u32Idx].pos.y, 
	// 			faces.data[u32Idx].pos.width, faces.data[u32Idx].pos.height, 
	// 			(int)faces.data[u32Idx].score);
	// #endif
	// 	if ((int)faces.data[u32Idx].score < 100)
	// 	{
	// 		//continue ;
	// 	}
	// 	pstObjRect[*pu32RectNum].s32X = faces.data[u32Idx].pos.x;
	// 	pstObjRect[*pu32RectNum].s32Y = faces.data[u32Idx].pos.y;
	// 	pstObjRect[*pu32RectNum].u32Width = (HI_U32)faces.data[u32Idx].pos.width;
	// 	pstObjRect[*pu32RectNum].u32Height = (HI_U32)faces.data[u32Idx].pos.height;
	// 	//stResult.au32Score[u32Idx] = (HI_U32)(faces.data[u32Idx].score * 100);
	// 	(*pu32RectNum)++;
	// }

	//printf("------777777777777777777------\n");
	iRet = HI_SUCCESS;

EXIT_WITH_FREE:
	HI_MPI_SYS_MmzFree(u64Bgr888PicBufPhyAddr, pcBgr888PicBufVirAddr);
EXIT:
	return iRet;
}

int SEETA_FACE2_ADPT_DestroyInstance(RT_HANDLE hInstance)
{
    int iRet = HI_SUCCESS;
	SEETA_FACE2_INSTANCE_CTX_S *hpstInstanceCtx = (SEETA_FACE2_INSTANCE_CTX_S*)hInstance;

	delete hpstInstanceCtx->hpclFaceEngine;
	free(hpstInstanceCtx);
	
    return iRet;
}


